from django.db import models
from django.db import connection
from lxml import etree
from django.contrib.auth.models import User
from registro.models import *
from codecs import open
import sys

#cambios: agregado campo nombre a ElementoDeCuestionario

class Dictionary(models.Model):
    name = models.CharField(max_length=100)
    def __unicode__(self):
        return str(self.name)
    
class DictionaryKeyVal(models.Model):
    Dictionary = models.ForeignKey(Dictionary)
    Key = models.CharField(max_length=200)
    Val = models.ForeignKey('GrupoDeOpciones')
    def __unicode__(self):
        return str('['+str(self.Key)+'] = '+str(self.Val))
        
class Cuestionario(models.Model):
    user = models.ForeignKey(User)
    nombre = models.CharField(max_length=200)
    activo = models.BooleanField()
    descripcion = models.CharField(max_length=1000, null=True)
    #codigo?
    
    GruposDeOpciones = Dictionary(name = 'xml_dictionary')
    def __unicode__(self):
        return str(self.nombre)
    def to_xml(self, path_directorio):
        #abrir archivo para escritura. ajustar a parametro
        nombre_archivo = str(self.id)+str(self.nombre)+'.xml'
        #path_archivo = '/home/oliver/Desktop/EUP/xml/xml_generated/'+nombre_archivo+'.xml'
        f = open(path_directorio+nombre_archivo, 'w', 'utf-8')
        #f.close()
        #f = open(path_archivo, 'w')
        xml_header = unicode('<?xml version="1.0" encoding="UTF-8"?>\n')
        xmlstring = ''
        xmlstring = xml_header + unicode('<cuestionario nombre=\"'+self.nombre+'\"')
        if self.descripcion != None:
            xmlstring += unicode(' descripcion=\"'+self.descripcion+'\"')
        #xmlstring += ' id=\"'+str(self.id)+'\"'
        xmlstring += unicode('>\n')
        for x in GrupoDePreguntas.objects.filter(cuestionario=self.id):
            xmlstring += unicode(x.to_xml())
        for x in Pregunta.objects.filter(cuestionario=self.id):
            if not x.grupo:
                xmlstring += unicode(x.to_xml())
        for x in GrupoDeOpciones.objects.filter(cuestionario=self.id):
            xmlstring += unicode(x.to_xml())
        xmlstring += unicode('</cuestionario>')
        #return str(xmlstring)
        #print xmlstring
        f.write(xmlstring)#.decode('utf-8')
        f.close()
        return (nombre_archivo)
        
    def xml(self, datos):  #tipo_fuente: 0: plain xml text 1: xml file
        #parser_remove_blank_spaces = etree.XMLParser(remove_blank_text=True)
        #if tipo_fuente == 1:
        try:
            xml = etree.parse(datos)
            xml = etree.tostring(xml, encoding='utf-8')
            xml = etree.XML(xml)
            #etree.tostring(xml)
            #elif tipo_fuente == 0:
                #xml=etree.XML(datos, parser_remove_blank_spaces)
                #etree.tostring(xml)
            if xml.tag == 'cuestionario':
        
                xml_nombre = xml.get('nombre')
                xml_descripcion = xml.get('descripcion')

                self.nombre = unicode(xml_nombre)
                self.descripcion = unicode(xml_descripcion)
                self.save()
                self.GruposDeOpciones.id = self.id
                #try:
                self.xml_grupodeopciones(xml, self)
                #except:
                #    print 'Error XML parser: unable to parse grupodeopciones!'
                #    return 'Error XML parser: unable to parse grupodeopciones!'
                for child in xml:
                    self.xml_cuestionario(child, self)
                #for child in xml:
                self.xml_regla_grupodeopciones(xml, self)
                self.save()
                #Eliminar el diccionario
                for d in Dictionary.objects.filter(id=self.id):
                    for dkv in DictionaryKeyVal.objects.filter(id=d.id):
                        dkv.delete()
                    d.delete()
            else:
                print 'Error XML parser: tag <cuestionario> not found!'
                return -1
        except:
            e = sys.exc_info()[1]
            print e
            print 'XMLSyntaxError'
            return -1
    def xml_grupodeopciones(self, xml, cuestionario):
        for child in xml:
            if child.tag == 'grupodeopciones':
                xml_go_nombre = child.get('nombre')
                go = GrupoDeOpciones(nombre=unicode(xml_go_nombre))
                go.cuestionario = cuestionario
                go.save()
                cuestionario.save()
                gokv = DictionaryKeyVal(Dictionary=self.GruposDeOpciones, Key=unicode(xml_go_nombre), Val=go)
                gokv.save()
                self.GruposDeOpciones.save()
    def xml_cuestionario(self, xml, cuestionario):
        if xml.tag == 'grupodepreguntas':
            xml_gp_nombre = xml.get('nombre')
            gp = GrupoDePreguntas(nombre=str(xml_gp_nombre))
            gp.cuestionario = cuestionario
            gp.save()
            cuestionario.save()
            for child in xml:
                self.xml_grupodepreguntas(child, gp)
            gp.regla = self.xml_regla(xml, gp, gp.cuestionario)
            gp.save()
        elif xml.tag == 'pregunta':
            xml_p_texto = xml.get('texto')
            xml_p_numero = xml.get('numero')    #puede NO tener numero?
            xml_p_nombre = xml.get('nombre')
            p = Pregunta(texto=unicode(xml_p_texto), numero=int(xml_p_numero), nombre=unicode(xml_p_nombre))
            p.cuestionario = cuestionario
            p.save()
            cuestionario.save()
            for child in xml:
                self.xml_pregunta(child, p)
            p.regla = self.xml_regla(xml, p, p.cuestionario)
            p.save()
    def xml_grupodepreguntas(self, xml, grupodepreguntas):
        if xml.tag == 'pregunta':
            xml_p_texto = xml.get('texto')
            xml_p_numero = xml.get('numero')
            xml_p_nombre = xml.get('nombre')
            p = Pregunta(texto=unicode(xml_p_texto), numero=int(xml_p_numero), nombre=unicode(xml_p_nombre))
            p.grupo = grupodepreguntas
            p.cuestionario = grupodepreguntas.cuestionario
            p.save()
            grupodepreguntas.save()
            for child in xml:
                self.xml_pregunta(child, p)
            p.regla = self.xml_regla(xml, p, p.cuestionario)
            p.save()
    def xml_pregunta(self, xml, pregunta):
        if xml.tag == 'opcion':
            xml_o_texto = xml.get('texto')
            xml_o_numero = xml.get('numero')
            xml_o_grupodeopciones = xml.get('grupo')
            xml_o_nombre = xml.get('nombre')
            o = Opcion(texto=unicode(xml_o_texto), numero=int(xml_o_numero), nombre=unicode(xml_o_nombre))
            o.pregunta = pregunta
            if xml_o_grupodeopciones != None:
                for x in DictionaryKeyVal.objects.filter(Dictionary=self.GruposDeOpciones):
                    if str(x.Key) == unicode(xml_o_grupodeopciones):
                        o.grupo = x.Val
            o.cuestionario = pregunta.cuestionario
            o.save()
            pregunta.save()
            o.regla = self.xml_regla(xml, o, o.cuestionario)
            o.save()
    def xml_regla_grupodeopciones(self, xml, c):
        for child in xml:
            if child.tag == 'grupodeopciones':
                xml_go_nombre = child.get('nombre')
                for go in GrupoDeOpciones.objects.filter(cuestionario=c):
                    if unicode(go.nombre) == unicode(xml_go_nombre):
                        go.regla = self.xml_regla(child, go, go.cuestionario)
                        go.save()
                        self.save()
    def xml_regla(self, xml, elemento_de_cuestionario, cuestionario):
        for child in xml:
            if child.tag == 'regla':
                regla_xml = child
                if regla_xml[0].tag == 'and':
                    return self.xml_operacionnaria('and', regla_xml[0], cuestionario)
                if regla_xml[0].tag == 'or':
                    return self.xml_operacionnaria('or', regla_xml[0], cuestionario)
                if regla_xml[0].tag == 'negacion':
                    return self.xml_negacion(regla_xml[0], cuestionario)
                if regla_xml[0].tag == 'variablelibre':
                    return self.xml_variablelibre(regla_xml[0], cuestionario)
    def xml_operacionnaria(self, operador, operandos_xml, cuestionario):
        #print 'xml_operacionnaria'
        #print 'operador = '+str(operador)
        #print 'operandos_xml = '+str(operandos_xml)
        #print 'cuestionario = '+str(cuestionario)
        on = OperacionNaria()
        if operador == 'and':
            on.op = Operador.objects.get(pk = 1)
            #print 'and'
        elif operador == 'or':
            on.op = Operador.objects.get(pk = 2)
            #print 'or'
        on.save()
        for o in operandos_xml:
            #print o
            if o.tag == 'and':
                ret_op = self.xml_operacionnaria('and', o, cuestionario)
                on.operandos.add(ret_op)
            elif o.tag == 'or':
                ret_op = self.xml_operacionnaria('or', o, cuestionario)
                on.operandos.add(ret_op)
            elif o.tag == 'negacion':
                ret_op = self.xml_negacion(o, cuestionario)
                on.operandos.add(ret_op)
            elif o.tag == 'variablelibre':
                ret_op = self.xml_variablelibre(o, cuestionario)
                on.operandos.add(ret_op)
            on.save()
        return on
    def xml_negacion(self, der_xml, cuestionario):
        '''
        if der_xml[0].tag == 'operacionbinaria':
            ret_der = self.xml_operacionbinaria(der_xml[0][0], der_xml[0][1], der_xml[0][2], cuestionario)
        '''
        if der_xml[0].tag == 'and':
            #print 'invoked and'
            ret_der = self.xml_operacionnaria('and', der_xml[0], cuestionario)
        if der_xml[0].tag == 'or':
            #print 'invoked or'
            ret_der = self.xml_operacionnaria('or', der_xml[0], cuestionario)
        elif der_xml[0].tag == 'negacion':
            ret_der = self.xml_negacion(der_xml[0], cuestionario)
        elif der_xml[0].tag == 'variablelibre':
            ret_der = self.xml_variablelibre(der_xml[0], cuestionario)
        neg = Negacion(der=ret_der)
        neg.save()
        return neg
    def xml_variablelibre(self, xml, cuest):
        ec = xml.get('elemento')
        cbv = xml.get('tipo')
        for c in ElementoDeCuestionario.objects.filter(cuestionario=cuest):
            if unicode(c.nombre) == unicode(ec):
                if cbv == 'activado':
                    vl = VariableLibre(elemento_de_cuestionario=c, campo_bool_volatil=CampoBoolVolatil.objects.get(pk=1))
                elif cbv == 'seleccionado':
                    vl = VariableLibre(elemento_de_cuestionario=c, campo_bool_volatil=CampoBoolVolatil.objects.get(pk=2))
                vl.save()
                return vl

class ElementoDeCuestionario(models.Model): #no es abstracta porque las entidades del motor de reglas haran referencia a ella
    nombre = models.CharField(max_length=200)
    cuestionario = models.ForeignKey(Cuestionario)
    campos = {'activado': True, 'seleccionado': False} #estos campos son volatiles (no se guardan en la base de datos, solo sirven como parametro para activar-desactivar elementos de un cuestionario
    nombre_de_clase = "ElementoDeCuestionario" #solucion provisoria porque isinstance() no me funcionan con los models de django
    regla = models.ForeignKey('EBF', null=True, on_delete=models.SET_NULL)
    def __unicode__(self):
        return str(self.nombre)
    def delete(self):
        if self.regla:
            self.regla.delete()
        super(ElementoDeCuestionario,self).delete()
    def activar(self):
        self.campos['activado'] = True
    def desactivar(self):
        self.campos['activado'] = False
    def is_activado(self):
        return self.campos['activado']
    def evaluar_regla(self):
        pass
    def convertirRegla(self, nuevoTipo):
        if nuevoTipo != "OperacionNaria" and nuevoTipo != "Negacion" and nuevoTipo != 'VariableLibre':
            raise ValueError("no se puede convertir a %s; no es un tipo de EBF" % nuevoTipo)
        #si ya es del mismo tipo, retorna
        if isinstance(self.regla, eval(nuevoTipo)):
            return
        #sino, elimina der y agrega nuevo
        if self.regla:
            self.regla.delete()
        nuevo=eval("%s()" % nuevoTipo)
        nuevo.save()
        self.regla = nuevo
        self.save()
    def getSubTipo(self):
        if Pregunta.objects.filter(pk=self.pk).exists():
            return self.pregunta
        if GrupoDePreguntas.objects.filter(pk=self.pk).exists():
            return self.grupodepreguntas
        if Opcion.objects.filter(pk=self.pk).exists():
            return self.opcion
        if GrupoDeOpciones.objects.filter(pk=self.pk).exists():
            return self.grupodeopciones
        raise ValueError("el ElementoDeCuestionario con id %d no corresponde a ningun subtipo de ElementoDeCuestionario" % self.pk)

def default_numero_pregunta():
    if Pregunta.objects.count() > 0:
        return Pregunta.objects.order_by('-numero')[0].numero + 1
    else:
        return 1

class Pregunta(ElementoDeCuestionario):
    #nombre = models.CharField(max_length=200)
    texto = models.CharField(max_length=500)
    grupo = models.ForeignKey('GrupoDePreguntas', null=True)
    numero = models.IntegerField(default=default_numero_pregunta)
    nombre_de_clase = "Pregunta" #solucion provisoria porque isinstance() no funciona con los models de django
    #orden(numero)?
    def __unicode__(self):
        return str(self.texto)
    def delete(self):
        pregsabajo = Pregunta.objects.filter(cuestionario=self.cuestionario, numero__gt=self.numero)
        for preg in pregsabajo:
            preg.numero=preg.numero-1
            preg.save()
        super(Pregunta,self).delete()
    def to_xml(self):
        xmlstring = unicode('')
        xmlstring = unicode('\t\t<pregunta nombre=\"'+self.nombre+'\" texto=\"'+self.texto+'\" numero=\"'+str(self.numero)+'\">\n')
        if self.regla == None:
            pass
        else:
            xmlstring += unicode('\t\t\t<regla>\n'+self.regla.to_xml()+'\t\t\t</regla>\n')
        for x in Opcion.objects.filter(pregunta=self.id):
            xmlstring += unicode(x.to_xml())
        xmlstring += unicode('\t\t</pregunta>\n')
        return unicode(xmlstring)
    def evaluar_regla(self, sesion):
        if self.regla == None:
            return True
        else:
            for x in OperacionNaria.objects.all():
                if(x.id == self.regla.id):
                    self.regla = OperacionNaria.objects.get(id = x.id)
            for x in Negacion.objects.all():
                if(x.id == self.regla.id):
                    self.regla = Negacion.objects.get(id = x.id)
            for x in VariableLibre.objects.all():
                if(x.id == self.regla.id):
                    self.regla = VariableLibre.objects.get(id = x.id)
            return self.regla.evaluar(sesion)
    def getSubTipo(self):
        return self

    def shiftOrdenUp(self):
        num = self.numero
        if not Pregunta.objects.filter(cuestionario=self.cuestionario.pk).filter(numero=num-1).exists():
            return
        otra = Pregunta.objects.filter(cuestionario=self.cuestionario.pk).get(numero=num-1)
        otra.numero += 1
        self.numero -= 1
        otra.save()
        self.save()

    def shiftOrdenDown(self):
        num = self.numero
        if not Pregunta.objects.filter(cuestionario=self.cuestionario.pk).filter(numero=num+1).exists():
            return
        otra = Pregunta.objects.filter(cuestionario=self.cuestionario.pk).get(numero=num+1)
        otra.numero -= 1
        self.numero += 1
        otra.save()
        self.save()

class GrupoDePreguntas(ElementoDeCuestionario):
    #nombre = models.CharField(max_length=200)
    nombre_de_clase = "GrupoDePreguntas" #solucion provisoria porque isinstance() no funciona con los models de django
    def __unicode__(self):
        return str(self.nombre)
    def to_xml(self):
        xmlstring = unicode('')
        xmlstring = unicode('\t<grupodepreguntas nombre=\"'+self.nombre+'\">\n')
        if self.regla == None:
            pass
        else:
            xmlstring += unicode('\t\t<regla>\n'+self.regla.to_xml()+'\t\t</regla>\n')
        for x in Pregunta.objects.filter(grupo=self.id):
            xmlstring += unicode(x.to_xml())
        xmlstring += unicode('\t</grupodepreguntas>\n')
        return unicode(xmlstring)
    def evaluar_regla(self, sesion):
        if self.regla == None:
            return True
        else:
            for x in OperacionNaria.objects.all():
                if(x.id == self.regla.id):
                    self.regla = OperacionNaria.objects.get(id = x.id)
            for x in Negacion.objects.all():
                if(x.id == self.regla.id):
                    self.regla = Negacion.objects.get(id = x.id)
            for x in VariableLibre.objects.all():
                if(x.id == self.regla.id):
                    self.regla = VariableLibre.objects.get(id = x.id)
            return self.regla.evaluar(sesion)

    def getSubTipo(self):
        return self

def default_numero_opcion():
    if Opcion.objects.count() > 0:
        return Opcion.objects.order_by('-numero')[0].numero + 1
    else:
        return 1
        
class Opcion(ElementoDeCuestionario):
    #nombre = models.CharField(max_length=200)
    campos = {'activado': True, 'seleccionado': False} #por default ninguna opcion esta seleccionada
    pregunta = models.ForeignKey(Pregunta)
    grupo = models.ForeignKey('GrupoDeOpciones', null=True)
    texto = models.CharField(max_length=500)
    numero = models.IntegerField(default=default_numero_opcion)
    nombre_de_clase = "Opcion" #solucion provisoria porque isinstance() no funciona con los models de django
    #orden(numero)?
    def __unicode__(self):
        return str(self.texto)
    def save(self, *args, **kwargs):
        self.cuestionario = self.pregunta.cuestionario
        super(Opcion, self).save(*args, **kwargs)
    def to_xml(self):
        xmlstring = unicode('')
        xmlstring = unicode('\t\t\t<opcion nombre=\"'+self.nombre+'\" texto=\"'+self.texto+'\" numero=\"'+str(self.numero)+'\"')
        if self.grupo == None:
            xmlstring += unicode('>')
        else:
            xmlstring += unicode(' grupo=\"'+self.grupo.__unicode__()+'\">\n')
        if self.regla == None:
            pass
        else:
            xmlstring += unicode('\t\t\t\t<regla>\n'+self.regla.to_xml()+'\t\t\t\t</regla>\n')
        xmlstring += unicode('\t\t\t</opcion>\n')
        return unicode(xmlstring)
    def evaluar_regla(self, sesion):
        if self.regla == None:
            return True
        else:
            for x in OperacionNaria.objects.all():
                if(x.id == self.regla.id):
                    self.regla = OperacionNaria.objects.get(id = x.id)
            for x in Negacion.objects.all():
                if(x.id == self.regla.id):
                    self.regla = Negacion.objects.get(id = x.id)
            for x in VariableLibre.objects.all():
                if(x.id == self.regla.id):
                    self.regla = VariableLibre.objects.get(id = x.id)
            return self.regla.evaluar(sesion)

    def getSubTipo(self):
        return self

class GrupoDeOpciones(ElementoDeCuestionario):
    #nombre = models.CharField(max_length=200)
    nombre_de_clase = "GrupoDeOpciones" #solucion provisoria porque isinstance() no funciona con los models de django
    def __unicode__(self):
        return str(self.nombre)
    def to_xml(self):
        xmlstring = unicode('')
        xmlstring = unicode('<grupodeopciones nombre=\"'+self.nombre+'\">\n')
        if self.regla == None:
            pass
        else:
            xmlstring += unicode('\t<regla>\n'+self.regla.to_xml()+'\t</regla>\n')
        xmlstring += unicode('</grupodeopciones>\n')
        return unicode(xmlstring)
    def evaluar_regla(self, sesion):
        if self.regla == None:
            return True
        else:
            for x in OperacionNaria.objects.all():
                if(x.id == self.regla.id):
                    self.regla = OperacionNaria.objects.get(id = x.id)
            for x in Negacion.objects.all():
                if(x.id == self.regla.id):
                    self.regla = Negacion.objects.get(id = x.id)
            for x in VariableLibre.objects.all():
                if(x.id == self.regla.id):
                    self.regla = VariableLibre.objects.get(id = x.id)
            return self.regla.evaluar(sesion)


    def getSubTipo(self):
        return self


class CampoBoolVolatil(models.Model):
    nombre = models.CharField(max_length=200)
    def __unicode__(self):
        return str(self.nombre)

class EBF(models.Model):
    def __unicode__(self):
        return ''
    def evaluar(self, sesion):
        pass
    def to_xml(self):
        if VariableLibre.objects.filter(id=self.id).count() == 1:
            for x in VariableLibre.objects.filter(id=self.id):
                return unicode(x.to_xml())
        elif Negacion.objects.filter(id=self.id).count() == 1:
            for x in Negacion.objects.filter(id=self.id):
                return unicode(x.to_xml())
        elif OperacionNaria.objects.filter(id=self.id).count() == 1:
            for x in OperacionNaria.objects.filter(id=self.id):
                return unicode(x.to_xml())
        else:
            return unicode('ATENCION: regla no definida')

    def getSubTipoEBF(self):
        if VariableLibre.objects.filter(pk=self.pk).exists():
            return self.variablelibre
        if Negacion.objects.filter(pk=self.pk).exists():
            return self.negacion
        if OperacionNaria.objects.filter(pk=self.pk).exists():
            return self.operacionnaria
        raise ValueError("La EBF con id %d no corresponde a ningun subtipo de EBF" % self.pk)
    
    #busca entre EBF y ElementoDecuestionario quien es su padre
    #puede retornar una EBF, un ElementoDeCuestionario o elevar un error si no tiene padre
    def getPadre(self):
        #buscar en negaciones
        qs = Negacion.objects.filter(der=self.pk)
        if qs.exists():
            return qs.get(der=self.pk)
        #buscar en operaciones n-arias
        if self.opn_set.exists():
            return self.opn_set.get()
        #buscar si es regla de algun elemento de cuestionario
        qs = ElementoDeCuestionario.objects.filter(regla=self.pk)
        if qs.exists():
            return qs.get(regla=self.pk)
        raise ValueError("La ebf %d no tiene padre" % self.pk)
    
    def getCuestionarioPadre(self):
        act = self
        while isinstance(act, EBF):
            act = act.getPadre()
        return act.cuestionario
    
    def getElementoDeCuestionarioPadre(self):
        act = self
        while isinstance(act,EBF):
            act= act.getPadre()
        return act.getSubTipo()
    
    
class VariableLibre(EBF):
    elemento_de_cuestionario = models.ForeignKey(ElementoDeCuestionario, null=True)
    campo_bool_volatil = models.ForeignKey(CampoBoolVolatil, null=True)
    nombre_de_clase = "variable_libre"
    def __unicode__(self):
        return str('('+str(self.elemento_de_cuestionario.__unicode__())+' '+str(self.campo_bool_volatil.__unicode__())+')')
    def evaluar(self, sesion):
        #return self.elemento_de_cuestionario.campos[self.campo_bool_volatil.nombre]
        if self.campo_bool_volatil.nombre == 'activado':
            for e in ElementosAcTemp.objects.all():
                if e.id == self.id:
                    return True
            return False
        elif self.campo_bool_volatil.nombre == 'seleccionado':
            s = SesionesDeCuestionarios.objects.get(id = sesion)
            for o in s.opciones.all():
                if o.id == self.elemento_de_cuestionario.id:
                    return True
            return False
                
            #for s in SesionesDeCuestionarios.objects.filter(id = sesion):
                #if  self.elemento_de_cuestionario.id == s.opciones:
                #    return True   
            #return False
    def to_xml(self):
        return unicode('\t<variablelibre elemento=\"'+str(self.elemento_de_cuestionario.__unicode__())+'\" tipo=\"'+str(self.campo_bool_volatil.__unicode__())+'\"/>\n')
    def getSubTipoEBF(self):
        return self
    def getPreguntasValidas(self):
        '''
        retorna un queryset con las preguntas a las que puede hacer referencia
        esta VariableLibre
        '''
        elem=self.getElementoDeCuestionarioPadre()
        if isinstance(elem,Opcion):
            preg=elem.pregunta
        elif not isinstance(elem,Pregunta):
            raise ValueError("la VariableLibre no es ni de una opcion ni de una pregunta")
        else:
            preg=elem
        pregsvalidas=Pregunta.objects.filter(cuestionario=preg.cuestionario,numero__lt=preg.numero).order_by('numero')
        return pregsvalidas
    

class Negacion(EBF):
    der = models.ForeignKey(EBF, related_name='neg_set', null=True, on_delete=models.SET_NULL)
    nombre_de_clase = "negacion"
    def __unicode__(self):
        return str('(not '+str(self.der.__unicode__())+')')
    def evaluar(self,sesion):
        ###########
        for x in OperacionNaria.objects.all():
            if(self.der.id == x.id):
                self.der = OperacionNaria.objects.get(id = x.id)
        for x in VariableLibre.objects.all():
            if(self.der.id == x.id):
                self.der = VariableLibre.objects.get(id = x.id)
        for x in Negacion.objects.all():
            if(self.der.id == x.id):
                self.der = Negacion.objects.get(id = x.id)
        ###########
        return not self.der.evaluar(sesion)
    def to_xml(self):
        return unicode('\t<negacion>\n'+self.der.to_xml()+'\t</negacion>\n')

    def delete(self):
        if self.der is not None:
            self.der.delete()
            self.der = None
        super(Negacion,self).delete()
    #convertirDer: convierte der al tipo de elemento especificado en nuevoTipo como cadena (debe ser OperacionBinaria,Negacion o VariableLibre)
    def convertirDer(self, nuevoTipo):
        if nuevoTipo != "OperacionNaria" and nuevoTipo != "Negacion" and nuevoTipo != 'VariableLibre':
            raise ValueError("no se puede convertir a %s; no es un tipo de EBF" % nuevoTipo)
        #si ya es del mismo tipo, retorna
        if isinstance(self.der, eval(nuevoTipo)):
            return
        #sino, elimina der y agrega nuevo
        if self.der:
            self.der.delete()
        nuevo=eval("%s()" % nuevoTipo)
        nuevo.save()
        self.der = nuevo
        self.save()
    def getSubTipoEBF(self):
        return self
        
        
    

class OperacionNaria(EBF):
    #izq = models.ForeignKey(EBF, related_name='opb_izq', null=True, on_delete=models.SET_NULL)
    #der = models.ForeignKey(EBF, related_name='opb_der', null=True, on_delete=models.SET_NULL)
    operandos = models.ManyToManyField(EBF, related_name='opn_set')
    op = models.ForeignKey('Operador', null=True)
    nombre_de_clase = "operacion_Naria"
    valores_operandos = []
    def __unicode__(self):
        #return str('('+str(self.izq.__unicode__())+' '+str(self.op.__unicode__())+' '+str(self.der.__unicode__())+')')
        unicode_str = str(self.op.__unicode__())
        for o in self.operandos.all():
            unicode_str += ' '
            unicode_str += str(o.__unicode__())
            unicode_str += ', '
        return unicode_str
    def delete(self):
        """
        override del metodo para que elimine a todos sus operando antes (que no queden EBF huerfanos)
        IMPORTANTE: segun la doc del django, para asegurar que este metodo se ejecute
                    no basta con llamar al delete de un queryset 
                    (eg. OperacionNaria.objects.all().delete())
                    , sino que iterar sobre el queryset y hacer delete de cada instancia
        """
        for operando in self.operandos.all():
            operando.delete()
        super(OperacionNaria,self).delete()
    def evaluar(self, sesion):
        self.valores_operandos = []
        #buscar en la bd todos los operandos
        for o in self.operandos.all():
            for x in OperacionNaria.objects.all():
                if o.id == x.id:
                    self.valores_operandos.append({'id': o.id, 'tipo_ebf': 'on', 'eval': None})
            for x in VariableLibre.objects.all():
                if o.id == x.id:
                    self.valores_operandos.append({'id': o.id, 'tipo_ebf': 'vl', 'eval': None})
            for x in Negacion.objects.all():
                if o.id == x.id:
                    self.valores_operandos.append({'id': o.id, 'tipo_ebf': 'ne', 'eval': None})
            o.save()
        self.save()
        #evaluar valores_operandos
        for vo in self.valores_operandos:
            if vo['tipo_ebf'] == 'vl':
                var = VariableLibre.objects.get(pk = vo['id'])
                vo['eval'] = var.evaluar(sesion)
            if vo['tipo_ebf'] == 'ne':
                var = Negacion.objects.get(pk = vo['id'])
                vo['eval'] = var.evaluar(sesion)
            if vo['tipo_ebf'] == 'on':
                var = OperacionNaria.objects.get(pk = vo['id'])
                vo['eval'] = var.evaluar(sesion)
        #evaluar los mismos de acuerdo al operador indicado
        if(self.op.id == 1):    #id 1 = and
            for vo in self.valores_operandos:
                if vo['eval'] == False:
                    return False
            return True
        if(self.op.id == 2):    #id 2 = or
            for vo in self.valores_operandos:
                if vo['eval'] == True:
                    return True
            return False
    def to_xml(self):
        operandos_xml_str = unicode('')
        for o in self.operandos.all():
            operandos_xml_str += unicode(o.to_xml())
        if self.op.id == 1:
            return unicode('\t<and>\n'+operandos_xml_str+'\t</and>\n')
        if self.op.id == 2:
            return unicode('\t<or>\n'+operandos_xml_str+'\t</or>\n')
    #convertirDer y convertirIzq: convierte hijo derecho e hijo izquierdo a nuevoTipo especificado como string
    def convertirOperando(self, nuevoTipo, hijo_pk=None):
        if nuevoTipo != "OperacionNaria" and nuevoTipo != "Negacion" and nuevoTipo != 'VariableLibre':
            raise ValueError("no se puede convertir a %s; no es un tipo de EBF" % nuevoTipo)
        if hijo_pk is not None:
            if not self.operandos.filter(pk=hijo_pk).exists():
                raise ValueError("no existe tal operando")
            self.operandos.get(pk=hijo_pk).delete()
        nuevo=eval("%s()" % nuevoTipo)
        nuevo.save()
        self.operandos.add(nuevo)
        self.save()
    def getSubTipoEBF(self):
        return self
    
        
class Operador(models.Model):
    simbolo = models.CharField(max_length=5)
    nombre = models.CharField(max_length=10)
    def __unicode__(self):
        return str(self.nombre)
    def to_xml(self):
        return unicode(self.nombre)
